#include <windows.h>
#include <setupapi.h>
#include <iostream>
#include <string>
#include <cfgmgr32.h>

#define _CRT_SECURE_NO_WARNINGS
std::wostream& operator << (std::wostream& wo, const GUID& guid)
{
    wo << guid.Data1 << guid.Data2 << guid.Data3 << guid.Data4 ;
    return wo;
}
std::wostream& operator << (std::wostream& wo, const SP_DEVINFO_DATA& dd)
{
	wo << "GUID: " << dd.ClassGuid << "  Handle: " << dd.DevInst << std::endl;
	return wo;
}
int enum_volumes()
{
    LPGUID guid = (LPGUID)&GUID_DEVINTERFACE_VOLUME;
    HDEVINFO hdevinfo = SetupDiGetClassDevsEx(guid, NULL, NULL,
                                              DIGCF_DEVICEINTERFACE | DIGCF_PRESENT,
                                              NULL, NULL, NULL);
    if( hdevinfo == INVALID_HANDLE_VALUE){
        return -1;
    }
    
    SP_DEVICE_INTERFACE_DATA did;
    did.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    DWORD index = 0;
    //enum all the interfaces
    while(SetupDiEnumDeviceInterfaces(hdevinfo,0,guid,index++,&did)){
        //show the interface guid
        std::wcout << did.InterfaceClassGuid << std::endl;
        //get the detailed information
        DWORD req_size = 0;
        BOOL r =  SetupDiGetDeviceInterfaceDetail(hdevinfo, &did, 0, 0, &req_size, 0);
        if( !r && GetLastError() != ERROR_INSUFFICIENT_BUFFER){
            std::wcout << L"Error" << std::endl;
            return -1;
        }
        char* buffer = new char[req_size];
        memset(buffer,0,req_size);
        PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA) buffer;
        pdidd->cbSize = sizeof(*pdidd);
        SP_DEVINFO_DATA sdd;
        sdd.cbSize = sizeof(SP_DEVINFO_DATA);
        if(SetupDiGetDeviceInterfaceDetail(hdevinfo,&did,pdidd,req_size,&req_size,&sdd)){
            wchar_t mountPoint[MAX_PATH];
            wchar_t volume[MAX_PATH];
            wcscpy(mountPoint,pdidd->DevicePath);
            wcscat(mountPoint,L"\\");
            //get the device that exposes this interface
            std::wcout << "Device: " << sdd.DevInst << std::endl;
            //get the mount point
            std::wcout << "Mount Point: " << mountPoint << std::endl;
            //get the volume name
            if(GetVolumeNameForVolumeMountPoint(mountPoint,volume,MAX_PATH)){
                std::wcout << "Volume: " << volume << std::endl << std::endl;
            }
            else{
                std::wcout << "Error" << std::endl;
            }
        }
		/*wchar_t deviceid[MAX_DEVICE_ID_LEN];
		CM_Get_Device_ID(hdevinfo, deviceid, MAX_DEVICE_ID_LEN*2, 0);
		std::wcout << "DeviceId: " << deviceid << std::endl;*/
    }

}
int enum_device_id(LPGUID guid)
{
    HDEVINFO hdevinfo = SetupDiGetClassDevsEx(guid, NULL, NULL,
                                              DIGCF_DEVICEINTERFACE | DIGCF_PRESENT,
                                              NULL, NULL, NULL);
    if( hdevinfo == INVALID_HANDLE_VALUE){
        return -1;
    }
    DWORD index = 0;
    SP_DEVINFO_DATA    device_data;
    device_data.cbSize = sizeof(SP_DEVINFO_DATA);
    char buffer[2560];
    while(SetupDiEnumDeviceInfo(hdevinfo, index++, &device_data)){
        DWORD required_size;
        if( SetupDiGetDeviceInstanceId(hdevinfo, &device_data,
                                        (PWSTR)buffer, 256,
                                        &required_size) != 0){
            std::wstring id((wchar_t*)buffer);
			std::wcout << "ID:  " << id << std::endl;
			std::wcout << device_data << std::endl;
			wchar_t relations[10240];
			relations[0] = 0;
			relations[1] = 0;
			if(CR_SUCCESS == CM_Get_Device_ID_List(id.c_str(), relations, 10240, CM_GETIDLIST_FILTER_REMOVALRELATIONS)){
				std::wcout << "ID_LIST: " << relations << std::endl;
			}
			else{
				std::wcout << "can not get device id list" << std::endl;
			}

        }
    }
    return 0;
}
DEVINST GetDrivesDevInstByDiskNumber(long DiskNumber, UINT DriveType)
{  
	GUID*   guid; 

	switch (DriveType)   
	{  
		case DRIVE_REMOVABLE:  
			//break;  
		case DRIVE_FIXED:
			guid = (GUID*)(void*)&GUID_DEVINTERFACE_DISK;  
			break;  
		case DRIVE_CDROM:  
			guid = (GUID*)(void*)&GUID_DEVINTERFACE_CDROM;  
			break;
		default:  
			return 0;  
	}  
	
	// Get device interface info set handle for all devices attached to system  
	HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);  
	
	if (hDevInfo == INVALID_HANDLE_VALUE) {  
		return 0;  
	}  
	
	// Retrieve a context structure for a device interface of a device  
	// information set.  
	DWORD					 dwIndex = 0;  
	SP_DEVICE_INTERFACE_DATA devInterfaceData = {0};
	devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
	BOOL					 bRet = FALSE;  
 
	
	BYTE							 Buf[1024];  
	PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;  
	SP_DEVICE_INTERFACE_DATA		 spdid; 
	spdid.cbSize = sizeof(spdid);
	SP_DEVINFO_DATA					 spdd;  
	DWORD							 dwSize;  

	while ( true ) 
	{  
		bRet = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex, &devInterfaceData);  
		if (!bRet) {  
			break;  
		}  
		
		SetupDiEnumInterfaceDevice(hDevInfo, NULL, guid, dwIndex, &spdid);  
		
		dwSize = 0;  
		SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL);  
		
		if ( dwSize!=0 && dwSize<=sizeof(Buf) ) 
		{  
			pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!  
			
			ZeroMemory((PVOID)&spdd, sizeof(spdd));  
			spdd.cbSize = sizeof(spdd);  
			
			long res = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd, dwSize, &dwSize, &spdd);  
			if ( res ) 
			{  
				HANDLE hDrive = CreateFile(pspdidd->DevicePath, 
										0, 
										FILE_SHARE_READ | FILE_SHARE_WRITE, 
										NULL, 
										OPEN_EXISTING, 
										NULL, 
										NULL);

				if ( hDrive != INVALID_HANDLE_VALUE ) 
				{  
					STORAGE_DEVICE_NUMBER sdn;  
					DWORD dwBytesReturned = 0;  
					res = DeviceIoControl(hDrive, 
										IOCTL_STORAGE_GET_DEVICE_NUMBER, 
										NULL, 
										0, 
										&sdn, 
										sizeof(sdn), 
										&dwBytesReturned, 
										NULL);  
					if ( res ) 
					{  
						if ( DiskNumber == (long)sdn.DeviceNumber ) 
						{  
							CloseHandle(hDrive);    
							SetupDiDestroyDeviceInfoList(hDevInfo);  
							return spdd.DevInst;  
						}
					}
					CloseHandle(hDrive); 
				} 
			} 
		} 
		dwIndex++;  
	} 

	SetupDiDestroyDeviceInfoList(hDevInfo);  
	return 0; 
} 
int GetDiskNumber(const wchar_t* drv)
{
	DWORD   dwBytesReturned = 0; 
	STORAGE_DEVICE_NUMBER sdn;
	std::wstring path(TEXT("\\\\.\\"));
	path += drv;
	HANDLE  hVolume = CreateFile(path.c_str(), 
		0, 
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL, 
		OPEN_EXISTING, 
		NULL, 
		NULL);
	if (hVolume == INVALID_HANDLE_VALUE){  
		return -1;  
	}   

	if( DeviceIoControl(hVolume,
		IOCTL_STORAGE_GET_DEVICE_NUMBER,
		NULL,
		0,
		&sdn,
		sizeof(sdn),
		&dwBytesReturned,
		NULL) ){  
			return sdn.DeviceNumber;  
	}  
	CloseHandle(hVolume);
	return -1;
}
std::wstring GetDeviceId(const wchar_t* drv)
{
	wchar_t drive[3];
	drive[0] = drv[0];
	drive[1] = drv[1];
	drive[2] = TEXT('\0');
	DEVINST devInst = GetDrivesDevInstByDiskNumber(GetDiskNumber(drive), DRIVE_REMOVABLE);
	if (devInst != NULL){
		wchar_t deviceid[MAX_DEVICE_ID_LEN];
		CM_Get_Device_ID(devInst, deviceid, MAX_DEVICE_ID_LEN*2, 0);
		return deviceid;
	}
	return std::wstring();

}
int main1()
{
	DEVINST devInst = GetDrivesDevInstByDiskNumber(GetDiskNumber(TEXT("G:")),DRIVE_REMOVABLE);
	if (devInst != NULL){
		wchar_t deviceid[MAX_DEVICE_ID_LEN];
		CM_Get_Device_ID(devInst, deviceid, MAX_DEVICE_ID_LEN*2, 0);
		std::wcout << "DeviceId: " << deviceid << std::endl;
	}
}
 void main()
{
    //enum_volumes();
	//std::cout << "Enum device ids:" << std::endl;
	std::cout << "Disk ids:" << std::endl;
    enum_device_id((LPGUID)&GUID_DEVINTERFACE_DISK);
	std::cout << "Volume ids:" << std::endl;
	enum_device_id((LPGUID)&GUID_DEVINTERFACE_VOLUME);
    system("PAUSE");
}

